﻿
#include "GLBBuilder.hpp"

#include <algorithm>
#include <cstring>
#include <mutex>
#include <array>
#include <filesystem>

#define NO_TRY_OUT_OF_RANGE 0

namespace GLBBuilderLib
{
	AddNodeException::AddNodeException(const std::string& what) noexcept :
		std::invalid_argument(what)
	{
	}

	SaveGLBException::SaveGLBException(const std::string& what) noexcept :
		std::ios::failure(what)
	{
	}

	GLBMeshData::GLBMeshData(const std::string& comp_name) :
		Name(comp_name)
	{
	}

	size_t GLBBuilder::BufferHash::operator()(const RawBinaryType& Buf) const
	{
		std::hash<std::string_view> hash;
		return hash(std::string_view(reinterpret_cast<const char*>(&Buf[0]), Buf.size()));
	}

	std::string base64::encode(const RawBinaryType& in)
	{
		std::string out;

		unsigned val = 0;
		int valb = -6;
		for (auto c : in) {
			val = (val << 8) + c;
			valb += 8;
			while (valb >= 0) {
				out.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(val >> valb) & 0x3F]);
				valb -= 6;
			}
		}
		if (valb > -6) out.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[((val << 8) >> (valb + 8)) & 0x3F]);
		while (out.size() % 4) out.push_back('=');
		return out;
	}

	RawBinaryType base64::decode(const std::string& in)
	{
		RawBinaryType out;
		out.reserve(8 * (1 + in.size() / 6));

		std::vector<int> T(256, -1);
		for (int i = 0; i < 64; i++) T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;

		unsigned val = 0;
		int valb = -8;
		for (auto c : in)
		{
			if (T[c] == -1) break;
			val = (val << 6) + T[c];
			valb += 6;
			if (valb >= 0)
			{
				out.push_back(uint8_t((val >> valb) & 0xFF));
				valb -= 8;
			}
		}
		return out;
	}

	std::string GLBBuilder::JsonFieldsToString(const std::vector<std::string>& JsonFields)
	{
		std::string sb;
		bool first = true;
		sb += "{";
		for (auto& field : JsonFields)
		{
			if (first) first = false;
			else sb += ", ";
			sb += field;
		}
		sb += "}";
		return sb;
	}

	template<typename ContainerType>
	std::string GLBBuilder::JsonArrayToString(const ContainerType& Data)
	{
		std::string sb;
		bool first = true;
		sb += "[";
		for (auto& item : Data)
		{
			if (first) first = false;
			else sb += ", ";
			if constexpr(std::is_arithmetic_v<typename ContainerType::value_type>)
				sb += std::to_string(item);
			else
				sb += item;
		}
		sb += "]";
		return sb;
	}

	template std::string GLBBuilder::JsonArrayToString(const std::vector<std::string>& JsonArray);
	template std::string GLBBuilder::JsonArrayToString(const std::set<size_t>& JsonArray);
	template std::string GLBBuilder::JsonArrayToString(const std::unordered_set<size_t>& JsonArray);

	static std::string tolower(const std::string& s)
	{
		std::string ret = s;
		std::transform(ret.begin(), ret.end(), ret.begin(), [](unsigned char c) { return std::tolower(c); });
		return ret;
	}

	template<typename T>
	void GLBBuilder::AddVectorToBuffer(const std::vector<T>& ToAdd, BufferPosition& AddedTo)
	{
		constexpr size_t alignment = 4;
		size_t size = ToAdd.size() * sizeof(T);
		size_t ins = Buffer.size();
		
		// 统计总的 Buffer 添加次数
		ArraysTotal++;

		if constexpr (ReuseBuffers)
		{
			auto BytesBufferInst = std::vector<uint8_t>();
			const auto* BytesBufferPtr = &BytesBufferInst;
			if constexpr (std::is_same_v<T, uint8_t>)
			{
				BytesBufferPtr = &ToAdd;
			}
			else
			{
				// 不是字节数组时，临时创建一个用于做 Key
				BytesBufferInst.resize(size);
				memcpy(&BytesBufferInst[0], &ToAdd[0], size);
			}

			auto& BytesBuffer = *BytesBufferPtr;

#if NO_TRY_OUT_OF_RANGE
			if (BufferIndexer.contains(BytesBuffer))
#else
			try
#endif
			{
				// 如果这个 Buffer 已经被存储过了，则直接返回其对应的位置
				AddedTo = BufferIndexer.at(BytesBuffer);
				ArraysReusage++;
				return;
			}
#if NO_TRY_OUT_OF_RANGE
			else
#else
			catch (const std::out_of_range&)
#endif
			{
				// 没被存储过，则只能老老实实把数据追加到 Buffer 末尾咯。
				AddedTo = { ins, size };
				BufferIndexer[BytesBuffer] = AddedTo; // 制作索引
			}
		}
		else
		{ // 不使用复用 Buffer 的策略时，所有内容追加到 Buffer 末尾
			AddedTo = { ins, size };
		}

		Buffer.resize(ins + size);
		memcpy(&Buffer[ins], &ToAdd[0], size);

		// 进行对齐
		size_t padded = ((Buffer.size() - 1) / alignment + 1) * alignment;
		while (Buffer.size() < padded) Buffer.push_back(0);
	}

	size_t GLBBuilder::BufferViewInfo::Hash::operator()(const BufferViewInfo& BV) const
	{
		return // 瞎几把写个哈希函数，速度要快。
			size_t(BV.buffer + 0) +
			size_t(BV.byteLength + 2) +
			size_t(BV.byteOffset + 16) +
			size_t(BV.target + 0) +
			size_t(BV.byteStride + 24);
	}

	size_t GLBBuilder::AccessorInfo::Hash::operator()(const AccessorInfo& c) const
	{
		auto strhash = std::hash<std::string>();
		return // 瞎几把写个哈希函数，速度要快。
			size_t(c.bufferView + 12) +
			size_t(c.componentType + 16) +
			size_t(c.count + 0) +
			strhash(c.type);
	}

	template<typename T>
	ptrdiff_t GLBBuilder::AddBufferView(const std::vector<T>& ToAdd, int Target, size_t byteStride)
	{
		auto lock = std::scoped_lock(*AddBufferViewMutex);
		
		std::string sb;
		auto BP = BufferPosition();
		AddVectorToBuffer(ToAdd, BP);

		// 此代码用于统计压缩相关
		BufferViewsTotal++;

		auto BV = BufferViewInfo
		{
			.buffer = 0,
			.byteLength = BP.byteLength,
			.byteOffset = BP.byteOffset,
			.target = Target,
			.byteStride = byteStride
		};

		ptrdiff_t Idx_BufferView = -1;
#if NO_TRY_OUT_OF_RANGE
		if (BufferViewIndexer.contains(BV))
#else
		try
#endif
		{
			// 查找完全相同的 bufferView，如果不相同则新建。
			Idx_BufferView = BufferViewIndexer.at(BV);
			BufferViewsReusage++;
		}
#if NO_TRY_OUT_OF_RANGE
		else
#else
		catch (const std::out_of_range&)
#endif
		{
			sb += "{";
			sb += k_v("buffer") + std::to_string(BV.buffer) + ", ";
			sb += k_v("byteLength") + std::to_string(BV.byteLength);
			if (BV.byteOffset)
			{
				sb += ", ";
				sb += k_v("byteOffset") + std::to_string(BV.byteOffset);
			}
			if (BV.target)
			{
				sb += ", ";
				sb += k_v("target") + std::to_string(BV.target);
			}
			if (BV.byteStride)
			{
				sb += ", ";
				sb += k_v("byteStride") + std::to_string(BV.byteStride);
			}
			sb += "}";

			JsonBufferViews.push_back(sb);
			Idx_BufferView = ptrdiff_t(JsonBufferViews.size() - 1);
			BufferViewIndexer[BV] = Idx_BufferView;
		}
		return Idx_BufferView;
	}

	ptrdiff_t GLBBuilder::AddAccessor(const AccessorInfo& a, const std::string& min_, const std::string& max_)
	{
		auto lock = std::scoped_lock(*AddAccessorMutex);

		// 此代码用于统计压缩相关
		AccessorsTotal++;

		ptrdiff_t Idx_Accessor = -1;
#if NO_TRY_OUT_OF_RANGE
		if (AccessorIndexer.contains(a))
#else
		try
#endif
		{
			Idx_Accessor = AccessorIndexer.at(a);
			AccessorsReusage++;
		}
#if NO_TRY_OUT_OF_RANGE
		else
#else
		catch (const std::out_of_range&)
#endif
		{
			std::string sb;
			sb += "{";
			sb += k_v("bufferView") + std::to_string(a.bufferView) + ",";
			sb += k_v("componentType") + std::to_string(a.componentType) + ","; // 浮点数类型
			if (min_.length()) sb += k_v("min") + min_ + ",";
			if (max_.length()) sb += k_v("max") + max_ + ",";
			sb += k_v("count") + std::to_string(a.count) + ",";
			sb += k_v("type") + "\"" + a.type + "\"";
			sb += "}";
			JsonAccessors.push_back(sb);
			Idx_Accessor = ptrdiff_t(JsonAccessors.size() - 1);
			AccessorIndexer[a] = Idx_Accessor;
		}
		return Idx_Accessor;
	}

	template<int L> // 各种维度数的向量
	ptrdiff_t GLBBuilder::AddAccessorVectors(const std::vector<vec<L, float>>& ToAdd)
	{
		auto a = AccessorInfo
		{
			.bufferView = AddBufferView(ToAdd, 34962, sizeof(ToAdd[0])),
			.componentType = 5126, // FLOAT
			.count = ToAdd.size()
		};
		auto v_p = vec<L, float>(-FLT_MAX);
		auto v_n = vec<L, float>( FLT_MAX);
		for (auto& v : ToAdd)
		{
			v_p = max(v_p, v);
			v_n = min(v_n, v);
		}
		auto min_str = std::array<std::string, L>();
		auto max_str = std::array<std::string, L>();
		switch (L)
		{
		case 1: a.type = "VEC1"; break;
		case 2: a.type = "VEC2"; break;
		case 3: a.type = "VEC3"; break;
		case 4: a.type = "VEC4"; break;
		}
		switch (L)
		{
		case 4: min_str[3] = std::to_string(v_n[3]); max_str[3] = std::to_string(v_p[3]);
		case 3: min_str[2] = std::to_string(v_n[2]); max_str[2] = std::to_string(v_p[2]);
		case 2: min_str[1] = std::to_string(v_n[1]); max_str[1] = std::to_string(v_p[1]);
		case 1: min_str[0] = std::to_string(v_n[0]); max_str[0] = std::to_string(v_p[0]);
		}
		return AddAccessor
		(
			a,
			JsonArrayToString(min_str),
			JsonArrayToString(max_str)
		);
	}

	ptrdiff_t GLBBuilder::AddAccessorQuats(const std::vector<quat>& ToAdd)
	{
		// 需要把 quat 的 w, x, y, z 顺序改为 x, y, z, w 顺序
		std::vector<vec4> Converted;
		Converted.resize(ToAdd.size());
#pragma omp parallel for
		for (ptrdiff_t i = 0; i < ptrdiff_t(ToAdd.size()); i++)
		{
			auto& q = ToAdd[i];
			Converted[i] = vec4(q.x, q.y, q.z, q.w);
		}

		return AddAccessor({
			.bufferView = AddBufferView(Converted, 0, sizeof(Converted[0])),
			.componentType = 5126, // FLOAT
			.count = Converted.size(),
			.type =	"VEC4"
			});
	}

	ptrdiff_t GLBBuilder::AddAccessorIntegers(const std::vector<int8_t>& ToAdd, int Target)
	{
		return AddAccessor({
			.bufferView = AddBufferView(ToAdd, Target),
			.componentType = 5120, //BYTE
			.count = ToAdd.size(),
			.type = "SCALAR"
			});
	}

	ptrdiff_t GLBBuilder::AddAccessorIntegers(const std::vector<uint8_t>& ToAdd, int Target)
	{
		return AddAccessor({
			.bufferView = AddBufferView(ToAdd, Target),
			.componentType = 5121, //UNSIGNED_BYTE
			.count = ToAdd.size(),
			.type = "SCALAR"
			});
	}

	ptrdiff_t GLBBuilder::AddAccessorIntegers(const std::vector<int16_t>& ToAdd, int Target, bool KeepSize)
	{
		if (KeepSize)
		{
			return AddAccessor({
				.bufferView = AddBufferView(ToAdd, Target),
				.componentType = 5122, //SHORT
				.count = ToAdd.size(),
				.type = "SCALAR"
				});
		}
		for (auto i : ToAdd)
		{
			if (i > 127 || i < -128) // 此处判断需不需要压缩为 8-bit
			{
				return AddAccessor({
					.bufferView = AddBufferView(ToAdd, Target),
					.componentType = 5122, //SHORT
					.count = ToAdd.size(),
					.type = "SCALAR"
					});
			}
		}
		BuffersCompressionStats["SHORT -> BYTE (2,1)"] += ToAdd.size();
		std::vector<int8_t> ToAdd8;
		ToAdd8.resize(ToAdd.size());
		for (size_t i = 0; i < ToAdd.size(); i++)
		{
			ToAdd8[i] = static_cast<int8_t>(ToAdd[i]);
		}
		return AddAccessorIntegers(ToAdd8, Target);
	}

	ptrdiff_t GLBBuilder::AddAccessorIntegers(const std::vector<uint16_t>& ToAdd, int Target, bool KeepSize)
	{
		if (KeepSize)
		{
			return AddAccessor({
				.bufferView = AddBufferView(ToAdd, Target),
				.componentType = 5123, //UNSIGNED_SHORT
				.count = ToAdd.size(),
				.type = "SCALAR"
				});
		}
		for (auto i : ToAdd)
		{
			if (i > 255) // 此处判断需不需要压缩为 8-bit
			{
				return AddAccessor({
					.bufferView = AddBufferView(ToAdd, Target),
					.componentType = 5123, //UNSIGNED_SHORT
					.count = ToAdd.size(),
					.type = "SCALAR"
					});
			}
		}
		BuffersCompressionStats["UNSIGNED_SHORT -> UNSIGNED_BYTE (2,1)"] += ToAdd.size();
		std::vector<uint8_t> ToAdd8;
		ToAdd8.resize(ToAdd.size());
		for (size_t i = 0; i < ToAdd.size(); i++)
		{
			ToAdd8[i] = static_cast<uint8_t>(ToAdd[i]);
		}
		return AddAccessorIntegers(ToAdd8, Target);
	}

	template<typename T> requires std::is_same_v<T, int32_t> || std::is_same_v<T, uint32_t>
	ptrdiff_t GLBBuilder::AddAccessorIntegers(const std::vector<T>& ToAdd, int Target, bool KeepSize)
	{
		if (KeepSize)
		{
			return AddAccessor({
				.bufferView = AddBufferView(ToAdd, Target),
				.componentType = 5125, //UNSIGNED_INT
				.count = ToAdd.size(),
				.type = "SCALAR"
				});
		}
		bool CanUseUBytes = true;
		for (auto i : ToAdd)
		{
			if (CanUseUBytes && (uint32_t(i) >= 256)) CanUseUBytes = false;
			if (uint32_t(i) >= 65536)
			{
				return AddAccessor({
					.bufferView = AddBufferView(ToAdd, Target),
					.componentType = 5125, //UNSIGNED_INT
					.count = ToAdd.size(),
					.type = "SCALAR"
					});
			}
		}
		if (CanUseUBytes)
		{
			BuffersCompressionStats["UNSIGNED_INT -> UNSIGNED_BYTE (4,1)"] += ToAdd.size();
			std::vector<uint8_t> ToAdd8;
			ToAdd8.resize(ToAdd.size());
			for (size_t i = 0; i < ToAdd.size(); i++)
			{
				ToAdd8[i] = static_cast<uint8_t>(ToAdd[i]);
			}
			return AddAccessorIntegers(ToAdd8, Target);
		}
		else
		{
			BuffersCompressionStats["UNSIGNED_INT -> UNSIGNED_SHORT (4,2)"] += ToAdd.size();
			std::vector<uint16_t> ToAdd16;
			ToAdd16.resize(ToAdd.size());
			for (size_t i = 0; i < ToAdd.size(); i++)
			{
				ToAdd16[i] = static_cast<uint16_t>(ToAdd[i]);
			}
			return AddAccessorIntegers(ToAdd16, Target, true);
		}
	}

	ptrdiff_t GLBBuilder::AddAccessorFloats(const std::vector<float>& ToAdd, bool KeepSize)
	{
		if (KeepSize)
		{
			return AddAccessor({
				.bufferView = AddBufferView(ToAdd),
				.componentType = 5126, //FLOAT
				.count = ToAdd.size(),
				.type = "SCALAR"
				});
		}
		// 先看看是不是全都是整数，是的话按整数来存
		bool CanUseBytes = true;
		for (size_t i = 0; i < ToAdd.size(); i++)
		{
			// 发现了一个不是整数的，或者没有压缩价值的数值，老老实实按浮点数存
			if (ToAdd[i] != std::roundf(ToAdd[i]) || ToAdd[i] > 32767.0f || ToAdd[i] < -32768.0f)
			{
				return AddAccessor({
					.bufferView = AddBufferView(ToAdd),
					.componentType = 5126, //FLOAT
					.count = ToAdd.size(),
					.type = "SCALAR"
					});
			}
			if (CanUseBytes && (ToAdd[i] > 127.0f || ToAdd[i] < -128.0f))
			{
				CanUseBytes = false;
			}
		}
		if (CanUseBytes)
		{
			BuffersCompressionStats["FLOAT -> BYTE (4,1)"] += ToAdd.size();
			std::vector<int8_t> ToAdd8;
			ToAdd8.resize(ToAdd.size());
			for (size_t i = 0; i < ToAdd.size(); i++)
			{
				ToAdd8[i] = static_cast<int8_t>(ToAdd[i]);
			}
			return AddAccessorIntegers(ToAdd8, 0);
		}
		else
		{
			BuffersCompressionStats["FLOAT -> SHORT (4,2)"] += ToAdd.size();
			std::vector<int16_t> ToAdd16;
			ToAdd16.resize(ToAdd.size());
			for (size_t i = 0; i < ToAdd.size(); i++)
			{
				ToAdd16[i] = static_cast<int16_t>(ToAdd[i]);
			}
			return AddAccessorIntegers(ToAdd16, 0, true);
		}
	}

	ptrdiff_t GLBBuilder::AddAccessorIndices(const std::vector<int8_t>& ToAdd)
	{
		return AddAccessorIntegers(ToAdd, 34963);
	}
	ptrdiff_t GLBBuilder::AddAccessorIndices(const std::vector<uint8_t>& ToAdd)
	{
		return AddAccessorIntegers(ToAdd, 34963);
	}
	ptrdiff_t GLBBuilder::AddAccessorIndices(const std::vector<int16_t>& ToAdd, bool KeepSize)
	{
		return AddAccessorIntegers(ToAdd, 34963, KeepSize);
	}
	ptrdiff_t GLBBuilder::AddAccessorIndices(const std::vector<uint16_t>& ToAdd, bool KeepSize)
	{
		return AddAccessorIntegers(ToAdd, 34963, KeepSize);
	}

	template<typename T> requires std::is_same_v<T, int32_t> || std::is_same_v<T, uint32_t>
	ptrdiff_t GLBBuilder::AddAccessorIndices(const std::vector<T>& ToAdd, bool KeepSize)
	{
		return AddAccessorIntegers(ToAdd, 34963, KeepSize);
	}

	template ptrdiff_t GLBBuilder::AddAccessorIntegers(const std::vector<int32_t>& ToAdd, int Target, bool KeepSize);
	template ptrdiff_t GLBBuilder::AddAccessorIntegers(const std::vector<uint32_t>& ToAdd, int Target, bool KeepSize);

	template ptrdiff_t GLBBuilder::AddAccessorIndices(const std::vector<int32_t>& ToAdd, bool KeepSize);
	template ptrdiff_t GLBBuilder::AddAccessorIndices(const std::vector<uint32_t>& ToAdd, bool KeepSize);

	bool GLBBuilder::IsPng(const RawBinaryType& data)
	{
		if (!memcmp(&data[0], "\x89PNG", 4)) return true;
		return false;
	}

	bool GLBBuilder::IsJpeg(const RawBinaryType& data)
	{
		if (!memcmp(&data[0], "\xff\xd8", 2)) return true;
		return false;
	}

	bool GLBBuilder::IsTarga(const RawBinaryType& data)
	{
		auto footer = "TRUEVISION-XFILE.";
		if (!memcmp(&data[data.size() - sizeof footer], footer, sizeof footer)) return true;
		return false;
	}

	bool GLBBuilder::IsPng(const std::string& filename, const RawBinaryType& data)
	{
		using namespace std::filesystem;

		auto p = path(filename);
		if (p.has_extension())
		{
			if (!memcmp(tolower(p.extension().string()).c_str(), ".png", 4)) return true;
		}
		else
		{
			return IsPng(data);
		}
		return false;
	}

	bool GLBBuilder::IsJpeg(const std::string& filename, const RawBinaryType& data)
	{
		using namespace std::filesystem;

		auto p = path(filename);
		if (p.has_extension())
		{
			auto ext = tolower(p.extension().string());
			if (!memcmp(ext.c_str(), ".jpg", 4)) return true;
			if (!memcmp(ext.c_str(), ".jpeg", 5)) return true;
		}
		else
		{
			return IsJpeg(data);
		}
		return false;
	}

	bool GLBBuilder::IsTarga(const std::string& filename, const RawBinaryType& data)
	{
		using namespace std::filesystem;

		auto p = path(filename);
		if (p.has_extension())
		{
			auto ext = tolower(p.extension().string());
			if (!memcmp(ext.c_str(), ".tga", 4)) return true;
		}
		else
		{
			return IsTarga(data);
		}
		return false;
	}

	size_t GLBBuilder::AddImage(size_t Idx_BufferView, const std::string& mimeType)
	{
		std::string sb;
		sb += "{";
		if (mimeType.size()) sb += k_v("mimeType") + "\"" + mimeType + "\", ";
		sb += k_v("bufferView") + std::to_string(Idx_BufferView);
		sb += "}";
		JsonImages.push_back(sb);
		return JsonImages.size() - 1;
	}

	size_t GLBBuilder::AddTexture(size_t Idx_Image)
	{
		std::string sb;
		sb += "{";
		sb += k_v("sampler") + "0,";
		sb += k_v("source") + std::to_string(Idx_Image);
		sb += "}";
		JsonTextures.push_back(sb);
		return JsonTextures.size() - 1;
	}

	size_t GLBBuilder::AddTexture(const TextureImage& Texture, bool HasOpacity, int JpegQuality)
	{
		RawBinaryType TextureBinary;

		if (HasOpacity)
			TextureBinary = Texture.SaveToPNG();
		else
			TextureBinary = Texture.SaveToJPG(JpegQuality);


#if NO_TRY_OUT_OF_RANGE
		if (TextureIndexer.contains(TextureBinary))
#else
		try
#endif
		{
			auto ret = TextureIndexer.at(TextureBinary);
			return ret;
		}
#if NO_TRY_OUT_OF_RANGE
		else
#else
		catch (const std::out_of_range&)
#endif
		{
			ptrdiff_t Idx_BufferView = -1;

			// 把这个纹理材质弄进 buffer 里
			Idx_BufferView = AddBufferView(TextureBinary, 0);

			// 添加到 images
			auto mimeType = IsPng(TextureBinary) ? "image/png" : "image/jpeg"; // 目前只认这两种

			size_t Idx_Image = AddImage(Idx_BufferView, mimeType);

			// 再添加到 textures
			size_t Idx_Texture = AddTexture(Idx_Image);

			// 记录索引
			TextureIndexer[TextureBinary] = Idx_Texture;
			return Idx_Texture;
		}
	}

	size_t GLBBuilder::AddTexture(const std::string& TexturePath, const RawBinaryType& TextureBinary, bool HasOpacity)
	{
#if NO_TRY_OUT_OF_RANGE
		if (TextureIndexer.contains(TextureBinary))
#else
		try
#endif
		{
			auto ret = TextureIndexer.at(TextureBinary);
			return ret;
		}
#if NO_TRY_OUT_OF_RANGE
		else
#else
		catch (const std::out_of_range&)
#endif
		{
			ptrdiff_t Idx_BufferView = -1;

			// 把这个纹理材质弄进 buffer 里
			Idx_BufferView = AddBufferView(TextureBinary, 0);

			// 添加到 images
			auto mimeType = IsPng(TextureBinary) ? "image/png" : "image/jpeg";

			size_t Idx_Image = AddImage(Idx_BufferView, mimeType);

			// 再添加到 textures
			size_t Idx_Texture = AddTexture(Idx_Image);

			// 记录索引
			TextureIndexer[TextureBinary] = Idx_Texture;
			return Idx_Texture;
		}
	}

	std::string GLBBuilder::MakeMaterialPBRMetallicRoughnessJSON
	(
		const vec4* baseColorFactor,
		ptrdiff_t Idx_baseColorTexture,
		double metallicFactor,
		double roughnessFactor,
		ptrdiff_t Idx_metalnessRoughnessTexture
	)
	{
		std::vector<std::string> fields;
		if (baseColorFactor && *baseColorFactor != vec4(1, 1, 1, 1))
		{
			auto sb = k_v("baseColorFactor")
			+ "["
			+ std::to_string(baseColorFactor->x) + ", "
			+ std::to_string(baseColorFactor->y) + ", "
			+ std::to_string(baseColorFactor->z) + ", "
			+ std::to_string(baseColorFactor->w)
			+ "]";
			fields.push_back(sb);
		}
		if (Idx_baseColorTexture >= 0)
		{
			auto sb = k_v("baseColorTexture")
			+ "{"
			+ k_v("index") + std::to_string(Idx_baseColorTexture)
			+ "}";
			fields.push_back(sb);
		}
		if (metallicFactor != 1.0)
		{
			auto sb = k_v("metallicFactor") + std::to_string(metallicFactor);
			fields.push_back(sb);
		}
		if (roughnessFactor != 1.0)
		{
			auto sb = k_v("roughnessFactor") + std::to_string(roughnessFactor);
			fields.push_back(sb);
		}
		if (Idx_metalnessRoughnessTexture >= 0)
		{
			auto sb = k_v("metalnessRoughnessTexture")
				+ "{"
				+ k_v("index") + std::to_string(Idx_metalnessRoughnessTexture)
				+ "}";
			fields.push_back(sb);
		}
		return k_v("pbrMetallicRoughness") + JsonFieldsToString(fields);
	}

	std::string GLBBuilder::MakeMaterialNormalTextureJSON
	(
		ptrdiff_t Idx_NormalTexture,
		double scale
	)
	{
		auto sb = k_v("normalTexture") + "{";
		if (scale != 1.0) sb += k_v("scale") + std::to_string(scale)+", ";
		sb += k_v("index") + std::to_string(Idx_NormalTexture);
		sb += "}";
		return sb;
	}

	std::string GLBBuilder::MakeMaterialOcclusionTextureJSON
	(
		ptrdiff_t Idx_OcclusionTexture,
		double strength
	)
	{
		auto sb = k_v("occlusionTexture") + "{";
		if (strength != 1.0) sb += k_v("strength") + std::to_string(strength)+", ";
		sb += k_v("index") + std::to_string(Idx_OcclusionTexture);
		sb += "}";
		return sb;
	}

	std::string GLBBuilder::MakeMaterialEmissiveTextureJSON
	(
		ptrdiff_t Idx_EmissiveTexture
	)
	{
		auto sb = k_v("emissiveTexture") + "{";
		sb += k_v("index") + std::to_string(Idx_EmissiveTexture);
		sb += "}";
		return sb;
	}

	size_t GLBBuilder::AddMaterial
	(
		const std::string& MaterialName,
		const vec4& BaseColor,
		double metalnessFactor,
		double roughnessFactor,
		ptrdiff_t Idx_Albedo_Texture,
		ptrdiff_t Idx_Normal_Texture,
		ptrdiff_t Idx_Emissive_Texture,
		ptrdiff_t Idx_Occlusion_Texture,
		ptrdiff_t Idx_MetallicRoughness_Texture
	)
	{
		auto Lock = ScopedLock(*AddMaterialLock);
		if (MaterialIndexer.contains(MaterialName))
		{
			return MaterialIndexer.at(MaterialName);
		}
		else
		{
			std::vector<std::string> fields;

			fields.push_back(std::string("\"name\": \"") + MaterialName + "\"");

			// 构件库材质
			ptrdiff_t Idx_Albedo = Idx_Albedo_Texture;
			ptrdiff_t Idx_Normal = Idx_Normal_Texture;
			ptrdiff_t Idx_Emissive = Idx_Emissive_Texture;
			ptrdiff_t Idx_Occlusion = Idx_Occlusion_Texture;
			ptrdiff_t Idx_MetallicRoughness = Idx_MetallicRoughness_Texture;

			if (true)
			{
				if (BaseColor.a <= 0.98f) fields.push_back("\"alphaMode\": \"BLEND\"");
				else fields.push_back("\"alphaMode\": \"MASK\"");
				fields.push_back(MakeMaterialPBRMetallicRoughnessJSON(&BaseColor, Idx_Albedo, metalnessFactor, roughnessFactor, Idx_MetallicRoughness));
			}
			if (Idx_Normal >= 0)
			{
				fields.push_back(MakeMaterialNormalTextureJSON(Idx_Normal, 1));
			}
			if (Idx_Emissive >= 0)
			{
				fields.push_back(MakeMaterialEmissiveTextureJSON(Idx_Emissive));
			}
			if (Idx_Occlusion >= 0)
			{
				fields.push_back(MakeMaterialOcclusionTextureJSON(Idx_Occlusion, 1));
			}

			auto MaterialIndex = JsonMaterials.size();
			MaterialIndexer[MaterialName] = MaterialIndex;
			JsonMaterials.push_back(JsonFieldsToString(fields));
			return MaterialIndex;
		}
	}

	ptrdiff_t GLBBuilder::AddMesh(const GLBMeshData& mesh)
	{
		auto numSubsets = ptrdiff_t(mesh.Subsets.size());
		if (!numSubsets) return -1;

		auto Idx_Positions = AddAccessorVectors(mesh.Positions);
		auto Idx_TexCoords = AddAccessorVectors(mesh.TexCoords);
		auto Idx_Normals = AddAccessorVectors(mesh.Normals);

		auto primitives = std::vector<std::string>();
		auto materials = std::vector<ptrdiff_t>();
		auto indices = std::vector<ptrdiff_t>();
		primitives.resize(numSubsets);
		materials.resize(numSubsets);
		indices.resize(numSubsets);
		// AddMaterial 无法并行。已经使用别的办法并行加入纹理材质了。
		for (ptrdiff_t i = 0; i < numSubsets; i++)
		{
			auto& subset = mesh.Subsets[i];
			materials[i] = AddMaterial(subset.Material);
		}
		// AddAccessors 无法并行，因为它要调用 AddBufferView 同样也是无法并行的。
		for (ptrdiff_t i = 0; i < numSubsets; i++)
		{
			auto& subset = mesh.Subsets[i];
			indices[i] = AddAccessorIndices(subset.TriangleIndices, DoCompressBuffersByRange ? false : true);
		}
#if !PROFILE_SingleThreadedMeshSubsetManipulation
#pragma omp parallel for
#endif
		for (ptrdiff_t i = 0; i < numSubsets; i++)
		{
			auto& subset = mesh.Subsets[i];
			if (subset.TriangleIndices.size() == 0) continue;
			ptrdiff_t Idx_Material = materials[i];
			ptrdiff_t Idx_Indices = indices[i];

			std::string sb;
			sb += "{";
			sb += k_v("attributes") + "{";
			sb += k_v("POSITION") + std::to_string(Idx_Positions) + ",";
			sb += k_v("NORMAL") + std::to_string(Idx_Normals) + ",";
			sb += k_v("TEXCOORD_0") + std::to_string(Idx_TexCoords);
			sb += "},";
			sb += k_v("indices") + std::to_string(Idx_Indices) + ",";
			sb += k_v("material") + std::to_string(Idx_Material);
			sb += "}";
			primitives[i] = sb;
		}
		std::string sb;
		sb += "{";
		if (mesh.Name.length()) sb += k_v("name") + "\"" + mesh.Name + "\", ";
		sb += k_v("primitives") + "[";
		bool first = true;
		for (auto& primitive : primitives)
		{
			if (!first) sb += ", ";
			else first = false;
			sb += primitive;
		}
		sb += "]";
		sb += "}";

		JsonMeshes.push_back(sb);
		return JsonMeshes.size() - 1;
	}

	template<typename ArrayType>
	size_t GLBBuilder::AddNode(ptrdiff_t MeshIndex, const ArrayType& ChildNodes, const vec3& Translation, const quat& Rotation, const vec3& Scale, const std::string& name)
	{
		std::string sb;

		sb += "{";
		if (MeshIndex != -1) sb += k_v("mesh") + std::to_string(MeshIndex)+", ";
		if (!name.empty()) sb += k_v("name") + "\"" + name + "\", ";
		if (ChildNodes.size()) sb += k_v("children") + JsonArrayToString(ChildNodes) + ", ";
		sb += k_v("rotation") + "[" + std::to_string(Rotation.x) + ", " + std::to_string(Rotation.y) + ", " + std::to_string(Rotation.z) + ", " + std::to_string(Rotation.w) + "], ";
		sb += k_v("scale") + "[" + std::to_string(Scale.x) + ", " + std::to_string(Scale.y) + ", " + std::to_string(Scale.z) + "], ";
		sb += k_v("translation") + "[" + std::to_string(Translation.x) + ", " + std::to_string(Translation.y) + ", " + std::to_string(Translation.z) + "]";
		sb += "}";

		if (strstr(sb.c_str(), "nan"))
		{
			throw AddNodeException("AddNode() failed: `nan` found in transformations.");
		}

		JsonNodes.push_back(sb);
		return JsonNodes.size() - 1;
	}

	template<typename ArrayType>
	size_t GLBBuilder::AddNode(ptrdiff_t MeshIndex, const ArrayType& ChildNodes, const mat4& Matrix, const std::string& name)
	{
		std::string sb;

		sb += "{";
		if (MeshIndex != -1) sb += k_v("mesh") + std::to_string(MeshIndex)+", ";
		if (!name.empty()) sb += k_v("name") + "\"" + name + "\", ";
		if (ChildNodes.size()) sb += k_v("children") + JsonArrayToString(ChildNodes) + ", ";
		sb += k_v("matrix") + "[";
		for (int i = 0; i < 16; i++)
		{
			if (i) sb += ", ";
			int row = i / 4;
			int col = i % 4;
			sb += std::to_string(Matrix[row][col]);
		}
		sb += "]}";

		if (strstr(sb.c_str(), "nan"))
		{
			throw AddNodeException("AddNode() failed: `nan` found in transformations.");
		}

		JsonNodes.push_back(sb);
		return JsonNodes.size() - 1;
	}

	template size_t GLBBuilder::AddNode(ptrdiff_t MeshIndex, const std::set<size_t>& ChildNodes, const vec3& Translation, const quat& Rotation, const vec3& Scale, const std::string& name);
	template size_t GLBBuilder::AddNode(ptrdiff_t MeshIndex, const std::set<size_t>& ChildNodes, const mat4& Matrix, const std::string& name);
	template size_t GLBBuilder::AddNode(ptrdiff_t MeshIndex, const std::unordered_set<size_t>& ChildNodes, const vec3& Translation, const quat& Rotation, const vec3& Scale, const std::string& name);
	template size_t GLBBuilder::AddNode(ptrdiff_t MeshIndex, const std::unordered_set<size_t>& ChildNodes, const mat4& Matrix, const std::string& name);
	template size_t GLBBuilder::AddNode(ptrdiff_t MeshIndex, const std::vector<size_t>& ChildNodes, const vec3& Translation, const quat& Rotation, const vec3& Scale, const std::string& name);
	template size_t GLBBuilder::AddNode(ptrdiff_t MeshIndex, const std::vector<size_t>& ChildNodes, const mat4& Matrix, const std::string& name);
	template size_t GLBBuilder::AddNode(ptrdiff_t MeshIndex, const std::list<size_t>& ChildNodes, const vec3& Translation, const quat& Rotation, const vec3& Scale, const std::string& name);
	template size_t GLBBuilder::AddNode(ptrdiff_t MeshIndex, const std::list<size_t>& ChildNodes, const mat4& Matrix, const std::string& name);

	static constexpr const char* ToString(GLBAnimInterpolationType LerpType)
	{
		switch (LerpType)
		{
		case GLBAnimInterpolationType::LINEAR: return "LINEAR";
		case GLBAnimInterpolationType::STEP: return "STEP";
		case GLBAnimInterpolationType::CUBICSPLINE: return "CUBICSPLINE";
		}
		return "<Unknown>";
	}

	void GLBAnimMovement::ValidateData() const
	{
		if (!TimeLine.size()) throw std::invalid_argument("When checking animation data by calling `GLBAnimMovement::ValidateData()`: Unable to add animation: No timeline was available.");
		if (!Translations.size() && !Rotations.size() && !Scales.size()) throw std::invalid_argument("When checking animation data by calling `GLBAnimMovement::ValidateData()`: Unable to add animation: No animations was available.");
		if ((Translations.size() && Translations.size() != TimeLine.size()) ||
			(Rotations.size() && Rotations.size() != TimeLine.size()) ||
			(TimeLine.size() && TimeLine.size() != TimeLine.size()))
		{
			throw std::invalid_argument("When checking animation data by calling `GLBAnimMovement::ValidateData()`: Unable to add animation: The TRS data size does not match the timeline data size.");
		}
	}

	bool GLBBuilder::BufferViewInfo::operator<(const BufferViewInfo& c) const
	{
		return
			buffer < c.buffer ||
			byteLength < c.byteLength ||
			byteOffset < c.byteOffset ||
			target < c.target ||
			byteStride < c.byteStride;
	}

	bool GLBBuilder::AccessorInfo::operator<(const AccessorInfo& c) const
	{
		return
			bufferView < c.bufferView ||
			componentType < c.componentType ||
			count < c.count ||
			type < c.type;
	}

	template<typename ArrayType>
	size_t GLBBuilder::AddAnimation
	(
		const ArrayType& Movements,
		const std::string& name,
		GLBAnimInterpolationType LerpType
	)
	{
		/*
		* https://github.khronos.org/glTF-Tutorials/gltfTutorial/gltfTutorial_006_SimpleAnimation.html
		* 
		* # 基础
		* 
		* ## 动画数据基础
		* 
		* 需要两个 accessor，一个是 time，一个是 TRS
		* - time 的 accessor 是 SCALAR，单位是秒。
		* 
		* # 指定动画
		* 
		* ## 动画数据来源
		* 
		* 需要 samplers 指定动画的来源
		* - input 指定 time，output 指定 TRS（都是 accessor）
		* - interpolation 可以指定 LINEAR
		* 
		* ## 动画绑定到 node
		* 
		* 有了动画的来源，需要指定动画如何应用到场景中，使用 channels
		* - 指定 sampler
		* - 指定 target
		*   - 指定 node
		*   - 指定 path （TRS 的类型）（对应 accessor 里面的数据）
		* 
		* */

		// 检查参数
		for (auto& anim : Movements)
		{
			anim.ValidateData();
		}

		auto AnimSamplers = std::vector<std::string>();
		auto AnimChannels = std::vector<std::string>();

		using MovementVectorType = std::vector<GLBAnimMovement>;

		std::unique_ptr<MovementVectorType> UPtrMV = nullptr;
		const MovementVectorType* PtrMV = nullptr;
		if constexpr (std::is_same_v<ArrayType, MovementVectorType>)
		{
			PtrMV = &Movements;
		}
		else
		{
			UPtrMV = std::make_unique<MovementVectorType>(Movements.cbegin(), Movements.cend());
			PtrMV = const_cast<const MovementVectorType*>(UPtrMV.get());
		}
		auto& MovementArray = *PtrMV;

#if !PROFILE_SingleThreadedAnimationGeneration
#pragma omp parallel for
#endif
		for (ptrdiff_t i = 0; i < ptrdiff_t(MovementArray.size()); i++)
		{
			auto& anim = MovementArray[i];
			auto Idx_Timeline = AddAccessorFloats(anim.TimeLine, DoCompressBuffersByRange ? false : true);

			if (Verbose)
			{
				auto NodesSet = std::set<size_t>();
				auto NodesStrings = std::vector<std::string>();

				NodesSet.insert(anim.Nodes.cbegin(), anim.Nodes.cend());
				for (auto& node : NodesSet) NodesStrings.push_back(std::to_string(node));

				std::cout << std::string("[INFO] Adding animations for node(s) ") + JsonArrayToString(NodesStrings) + ".\n";
			}

			auto& Translates = anim.Translations;
			auto& Rotations = anim.Rotations;
			auto& Scales = anim.Scales;

			ptrdiff_t Idx_Translations = -1;
			ptrdiff_t Idx_Rotations = -1;
			ptrdiff_t Idx_Scales = -1;

			// 动画数据来源
			if (Translates.size())
			{
				std::string ssb;
				ssb += "{";
				ssb += k_v("input") + std::to_string(Idx_Timeline) + ",";
				ssb += k_v("output") + std::to_string(AddAccessorVectors(Translates)) + ",";
				ssb += k_v("interpolation", ToString(LerpType));
				ssb += "}";
#if !PROFILE_SingleThreadedAnimationGeneration
#pragma omp critical
#endif
				{
					Idx_Translations = AnimSamplers.size();
					AnimSamplers.push_back(ssb);
				}
			}
			if (Rotations.size())
			{
				std::string ssb;
				ssb += "{";
				ssb += k_v("input") + std::to_string(Idx_Timeline) + ",";
				ssb += k_v("output") + std::to_string(AddAccessorQuats(Rotations)) + ",";
				ssb += k_v("interpolation", ToString(LerpType));
				ssb += "}";
#if !PROFILE_SingleThreadedAnimationGeneration
#pragma omp critical
#endif
				{
					Idx_Rotations = AnimSamplers.size();
					AnimSamplers.push_back(ssb);
				}
			}
			if (Scales.size())
			{
				std::string ssb;
				ssb += "{";
				ssb += k_v("input") + std::to_string(Idx_Timeline) + ",";
				ssb += k_v("output") + std::to_string(AddAccessorVectors(Scales)) + ",";
				ssb += k_v("interpolation", ToString(LerpType));
				ssb += "}";
#if !PROFILE_SingleThreadedAnimationGeneration
#pragma omp critical
#endif
				{
					Idx_Scales = AnimSamplers.size();
					AnimSamplers.push_back(ssb);
				}
			}
			// 动画通道
			for (auto node : anim.Nodes)
			{ // 对每个 node 都要添加一遍
				if (Translates.size())
				{
					std::string ssb;
					ssb += "{";
					ssb += k_v("sampler") + std::to_string(Idx_Translations) + ",";
					ssb += k_v("target") + "{";
					ssb += k_v("node") + std::to_string(node) + ",";
					ssb += k_v("path", "translation");
					ssb += "}}";
#if !PROFILE_SingleThreadedAnimationGeneration
#pragma omp critical
#endif
					AnimChannels.push_back(ssb);
				}
				if (Rotations.size())
				{
					std::string ssb;
					ssb += "{";
					ssb += k_v("sampler") + std::to_string(Idx_Rotations) + ",";
					ssb += k_v("target") + "{";
					ssb += k_v("node") + std::to_string(node) + ",";
					ssb += k_v("path", "rotation");
					ssb += "}}";
#if !PROFILE_SingleThreadedAnimationGeneration
#pragma omp critical
#endif
					AnimChannels.push_back(ssb);
				}
				if (Scales.size())
				{
					std::string ssb;
					ssb += "{";
					ssb += k_v("sampler") + std::to_string(Idx_Scales) + ",";
					ssb += k_v("target") + "{";
					ssb += k_v("node") + std::to_string(node) + ",";
					ssb += k_v("path", "scale");
					ssb += "}}";
#if !PROFILE_SingleThreadedAnimationGeneration
#pragma omp critical
#endif
					AnimChannels.push_back(ssb);
				}
			}
		}

		std::string sb;
		sb += "{";
		if (name.length()) sb += k_v("name") + "\"" + name + "\",";
		sb += k_v("samplers") + JsonArrayToString(AnimSamplers) + ","; // 动画数据来源
		sb += k_v("channels") + JsonArrayToString(AnimChannels); // 动画通道
		sb += "}";

		auto Lock = ScopedLock(*AddAnimationLock);
		auto Ret = JsonAnimations.size();
		JsonAnimations.push_back(sb);
		return Ret;
	}

	template size_t GLBBuilder::AddAnimation(const std::list<GLBAnimMovement>& Movements, const std::string& name, GLBAnimInterpolationType LerpType);
	template size_t GLBBuilder::AddAnimation(const std::set<GLBAnimMovement>& Movements, const std::string& name, GLBAnimInterpolationType LerpType);
	template size_t GLBBuilder::AddAnimation(const std::unordered_set<GLBAnimMovement>& Movements, const std::string& name, GLBAnimInterpolationType LerpType);
	template size_t GLBBuilder::AddAnimation(const std::vector<GLBAnimMovement>& Movements, const std::string& name, GLBAnimInterpolationType LerpType);

	void GLBBuilder::ReportStatistics() const
	{
		// 不论是否详细模式，都汇报
		if (ArraysTotal)
			std::cout << std::string("[INFO] Arrays reusage: ") + std::to_string(ArraysReusage) + " arrays reused out of total " + std::to_string(ArraysTotal) + " arrays added (" + std::to_string(ArraysReusage * 100 / ArraysTotal) + " %)\n";
		else
			std::cout << "[INFO] No buffers added yet.\n";

		if (BufferViewsTotal)
			std::cout << std::string("[INFO] Buffer view reusage: ") + std::to_string(BufferViewsReusage) + " buffer views reused out of total " + std::to_string(BufferViewsTotal) + " buffer views added (" + std::to_string(BufferViewsReusage * 100 / BufferViewsTotal) + " %)\n";
		else
			std::cout << "[INFO] No buffer views added yet.\n";

		if (AccessorsTotal)
			std::cout << std::string("[INFO] Accessor reusage: ") + std::to_string(AccessorsReusage) + " accessors reused out of total " + std::to_string(AccessorsTotal) + " accessors added (" + std::to_string(AccessorsReusage * 100 / AccessorsTotal) + " %)\n";
		else
			std::cout << "[INFO] No accessors added yet.\n";

		if (BuffersCompressionStats.size())
		{
			for (auto& k_v : BuffersCompressionStats)
			{
				auto& conv = k_v.first;
				auto unit = k_v.second;
				int su1 = -1, su2 = -1;
				auto* pch = strchr(conv.c_str(), '(');
				if (pch) if (sscanf(pch, "(%d,%d)", &su1, &su2) != 1) {}
				
				auto info = std::string("[INFO] Range based compression used: ") + conv + ": compressed " + std::to_string(unit);
				if (su1 != -1 && su2 != -1) info += " numbers, reduced " + std::to_string(unit * su1 - unit * su2) + " bytes of size.\n";
				else info += " numbers.\n";
				std::cout << info;
			}
		}
	}

	GLBBuilder::GLBBuilder() :
		GLBBuilder(false)
	{
	}

	GLBBuilder::GLBBuilder(bool Verbose) :
		Verbose(Verbose)
	{
		JsonAsset = std::vector<std::string>
		{
			"\"generator\": \"SannongData Cyber AI Modeler System\"",
			"\"version\":\"2.0\""
		};

		JsonSamplers =
			std::vector<std::string>{
				JsonFieldsToString({
					std::vector<std::string>{
					"\"magFilter\": 9729", 
					"\"minFilter\": 9987"
		}})};
	}

	size_t GLBBuilder::AddScene(const std::string& name, const std::unordered_set<size_t>& nodes)
	{
		std::vector<std::string> fields;
		if (!name.empty()) fields.push_back(k_v("name") + "\"" + name + "\"");
		if (nodes.size()) fields.push_back(k_v("nodes") + JsonArrayToString(nodes));

		JsonScenes.push_back(JsonFieldsToString(fields));
		return JsonScenes.size() - 1;
	}

	std::string GLBBuilder::BuffersJSON(bool B64Buffer, const std::string& binSaveTo) const
	{
		std::string sb;
		sb += "[";
		sb += "{";
		if (B64Buffer)
		{
			sb += k_v("uri")
				+ "\"data:application/octet-stream;base64,"
				+ base64::encode(Buffer)
				+ "\",";
		}
		if (binSaveTo.length())
		{
			auto filename = std::filesystem::path(binSaveTo).filename().string();
			sb += k_v("uri") + "\"" + filename + "\", ";
		}
		sb += k_v("byteLength") + std::to_string(Buffer.size());
		sb += "}";
		sb += "]";
		if (binSaveTo.length())
		{
			std::ofstream binf;
			binf.exceptions(std::ofstream::badbit | std::ofstream::failbit);
			binf.open(binSaveTo, std::ios::binary);
			binf.write(reinterpret_cast<const char*>(&Buffer[0]), Buffer.size());
		}
		return sb;
	}

	std::string GLBBuilder::JsonExtrasSectionToString(const JsonExtrasSectionType& SectionMap)
	{
		auto ret = std::string("{");

		bool is_1st = true;
		for (auto& KeyValue : SectionMap)
		{
			if (is_1st) is_1st = false;
			else ret += ",";

			auto& Key = KeyValue.first;
			auto& Val = KeyValue.second;

			ret += "\"";
			ret += Key;
			ret += "\": ";
			ret += Val;
		}

		ret += "}";
		return ret;
	}

	std::string GLBBuilder::JsonExtrasToString(const JsonExtrasType& ExtrasMap)
	{
		auto ret = std::string("[{");

		bool is_1st = true;
		for (auto& Section : ExtrasMap)
		{
			if (is_1st) is_1st = false;
			else ret += ",";

			auto& Key = Section.first;
			auto& Val = Section.second;

			ret += "\"";
			ret += Key;
			ret += "\": ";
			ret += JsonExtrasSectionToString(Val);
		}

		ret += "}]";
		return ret;
	}

	std::string GLBBuilder::GetJSONStr(bool B64Buffer, const std::string& binSaveTo) const
	{
		std::map<std::string, std::string> JSON;

		JSON["bufferViews"] = JsonArrayToString(JsonBufferViews);
		JSON["meshes"] = JsonArrayToString(JsonMeshes);
		JSON["accessors"] = JsonArrayToString(JsonAccessors);
		JSON["nodes"] = JsonArrayToString(JsonNodes);
		JSON["materials"] = JsonArrayToString(JsonMaterials);
		JSON["asset"] = JsonFieldsToString(JsonAsset);
		JSON["scene"] = "0";
		JSON["scenes"] = JsonArrayToString(JsonScenes);
		JSON["buffers"] = BuffersJSON(B64Buffer, binSaveTo);
		JSON["samplers"] = JsonArrayToString(JsonSamplers);

		if (JsonTextures.size())
		{
			JSON["textures"] = JsonArrayToString(JsonTextures);
		}

		if (JsonImages.size())
		{
			JSON["images"] = JsonArrayToString(JsonImages);
		}

		if (JsonAnimations.size())
		{ // 只在有动画的时候插入这部分内容
			JSON["animations"] = JsonArrayToString(JsonAnimations);
		}
		
		// 特殊信息，用于传递数据给前端
		if (JsonExtras.size())
		{
			JSON["extras"] = JsonExtrasToString(JsonExtras);
		}

		std::string sb;
		bool is_first = true;
		sb += "{";
		for (auto& kv : JSON)
		{
			if (is_first) is_first = false;
			else sb += ", ";
			sb += "\"" + kv.first + "\": " + kv.second;
		}
		sb += "}";
		return sb;
	}

	void GLBBuilder::SaveGLTF(const std::string& JsonSaveTo, const std::string& binSaveTo) const
	{
		try
		{
			std::ofstream jsonf;
			jsonf.exceptions(std::ofstream::badbit | std::ofstream::failbit);
			jsonf.open(JsonSaveTo, std::ios::binary);
			if (!binSaveTo.empty())
				jsonf << GetJSONStr(false, binSaveTo);
			else // 不提供 bin 文件路径的时候，Buffer 以 Base64 的形式内嵌。
				jsonf << GetJSONStr(true, "");
		}
		catch (const std::ios::failure& e)
		{
			std::string except;
			if (binSaveTo.length())
				except = std::string("Could not save GLTF/bin to \"") + JsonSaveTo + "\"/\"" + binSaveTo + "\": " + e.what();
			else
				except = std::string("Could not save GLTF to \"") + JsonSaveTo + "\" with embedded binary as Base64 URI: " + e.what();
			throw SaveGLBException(except);
		}
	}

	std::vector<uint8_t> GLBBuilder::GenGLBFileBytes(bool B64Buffer) const
	{
		std::string JSONStr = GetJSONStr(B64Buffer, "");
		std::vector<uint8_t> bytes;
		uint32_t magic = 0x46546C67;
		uint32_t version = 2;
		uint32_t length = uint32_t(12 + 4 + 4 + JSONStr.length());
		if (!B64Buffer)
		{
			length += 4 + 4 + uint32_t(Buffer.size());
		}
		bytes.resize(length);

		// GLB 头部
		memcpy(&bytes[0], &magic, 4);
		memcpy(&bytes[4], &version, 4);
		memcpy(&bytes[8], &length, 4);

		// JSON 数据
		uint32_t ChunkLength = uint32_t(JSONStr.length());
		uint32_t ChunkType = 0x4E4F534A;
		memcpy(&bytes[12], &ChunkLength, 4);
		memcpy(&bytes[16], &ChunkType, 4);
		memcpy(&bytes[20], JSONStr.c_str(), JSONStr.size());

		// Buffer 数据
		if (!B64Buffer)
		{
			ChunkLength = uint32_t(Buffer.size());
			ChunkType = 0x004E4942;
			size_t i = 20 + JSONStr.size();
			memcpy(&bytes[i + 0], &ChunkLength, 4);
			memcpy(&bytes[i + 4], &ChunkType, 4);
			memcpy(&bytes[i + 8], &Buffer[0], Buffer.size());
		}
		return bytes;
	}

	void GLBBuilder::SaveGLB(const std::string& SaveTo, bool B64Buffer) const
	{
		try
		{
			std::ofstream glbf;
			glbf.exceptions(std::ofstream::badbit | std::ofstream::failbit);
			glbf.open(SaveTo, std::ios::binary);
			auto bytes = GenGLBFileBytes(B64Buffer);
			glbf.write(reinterpret_cast<const char*>(&bytes[0]), bytes.size());
		}
		catch (const std::ios::failure& e)
		{
			throw SaveGLBException(std::string("Could not save GLB to \"") + SaveTo + "\": " + e.what());
		}
	}

	template<typename T>
	static std::string Any_T_ToString(const T& v)
	{
		if constexpr (std::is_same_v<T, bool>) return v ? "true" : "false";
		else if constexpr (!std::is_same_v<T, std::string>) return std::to_string(v);
		else return std::string("\"") + v + "\"";
	}

	template<typename CofT>
	static std::string Any_T_Array_ToString(const CofT& v)
	{
		using T = CofT::value_type;
		auto ret = std::string("[");
		bool is_1st = true;
		for (auto item : v)
		{
			if (is_1st) is_1st = false;
			else ret += ",";
			if constexpr (std::is_same_v<T, bool>)
			{
				ret += item ? "true" : "false";
			}
			else if constexpr (std::is_same_v<T, std::string>)
			{
				ret += item;
			}
			else if constexpr (std::is_arithmetic_v<T>)
			{
				ret += std::to_string(item);
			}
			else
			{
				ret += "<?>";
			}
		}
		ret += "]";
		return ret;
	}

	void GLBBuilder::SetExtrasDataRaw(const std::string& SectionName, const std::string& KeyName, const std::string& Value)
	{
		auto& Section = JsonExtras[SectionName];

#if NO_TRY_OUT_OF_RANGE
		if (Section.contains(KeyName))
#else
		try
#endif
		{
			auto& Prev = Section.at(KeyName);
			std::cerr << std::string("[WARN]") + "In section \"" + SectionName + "\": Overwriting \"" + KeyName + "\" to value `" + Value + "` (previous is `" + Prev + "`)";
			Prev = Value;
		}
#if NO_TRY_OUT_OF_RANGE
		else
#else
		catch (const std::out_of_range&)
#endif
		{
			Section[KeyName] = Value;
		}
	}

	template<typename T> requires std::is_arithmetic_v<T> || std::is_same_v<T, std::string>
	void GLBBuilder::SetExtrasData(const std::string& SectionName, const std::string& KeyName, const T& Value)
	{
		SetExtrasDataRaw(SectionName, KeyName, Any_T_ToString(Value));
	}

	template<typename CofT>
	void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const CofT& Value)
	{
		SetExtrasDataRaw(SectionName, KeyName, Any_T_Array_ToString(Value));
	}

	template void GLBBuilder::SetExtrasData(const std::string& SectionName, const std::string& KeyName, const bool& Value);
	template void GLBBuilder::SetExtrasData(const std::string& SectionName, const std::string& KeyName, const int8_t& Value);
	template void GLBBuilder::SetExtrasData(const std::string& SectionName, const std::string& KeyName, const int16_t& Value);
	template void GLBBuilder::SetExtrasData(const std::string& SectionName, const std::string& KeyName, const int32_t& Value);
	template void GLBBuilder::SetExtrasData(const std::string& SectionName, const std::string& KeyName, const int64_t& Value);
	template void GLBBuilder::SetExtrasData(const std::string& SectionName, const std::string& KeyName, const uint8_t& Value);
	template void GLBBuilder::SetExtrasData(const std::string& SectionName, const std::string& KeyName, const uint16_t& Value);
	template void GLBBuilder::SetExtrasData(const std::string& SectionName, const std::string& KeyName, const uint32_t& Value);
	template void GLBBuilder::SetExtrasData(const std::string& SectionName, const std::string& KeyName, const uint64_t& Value);
	template void GLBBuilder::SetExtrasData(const std::string& SectionName, const std::string& KeyName, const float& Value);
	template void GLBBuilder::SetExtrasData(const std::string& SectionName, const std::string& KeyName, const double& Value);
	template void GLBBuilder::SetExtrasData(const std::string& SectionName, const std::string& KeyName, const std::string& Value);

	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::list<bool>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::list<int8_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::list<int16_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::list<int32_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::list<int64_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::list<uint8_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::list<uint16_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::list<uint32_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::list<uint64_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::list<float>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::list<double>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::list<std::string>& Value);

	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::vector<bool>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::vector<int8_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::vector<int16_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::vector<int32_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::vector<int64_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::vector<uint8_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::vector<uint16_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::vector<uint32_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::vector<uint64_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::vector<float>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::vector<double>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::vector<std::string>& Value);

	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::set<bool>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::set<int8_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::set<int16_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::set<int32_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::set<int64_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::set<uint8_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::set<uint16_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::set<uint32_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::set<uint64_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::set<float>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::set<double>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::set<std::string>& Value);

	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::unordered_set<bool>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::unordered_set<int8_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::unordered_set<int16_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::unordered_set<int32_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::unordered_set<int64_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::unordered_set<uint8_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::unordered_set<uint16_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::unordered_set<uint32_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::unordered_set<uint64_t>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::unordered_set<float>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::unordered_set<double>& Value);
	template void GLBBuilder::SetExtrasDataArray(const std::string& SectionName, const std::string& KeyName, const std::unordered_set<std::string>& Value);
}
